/*
Copyright (C) 2009 Grant Slender

This file is part of OFCGWT.
http://code.google.com/p/ofcgwt/

OFCGWT is free software: you can redistribute it and/or modify
it under the terms of the Lesser GNU General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.

OFCGWT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

See <http://www.gnu.org/licenses/lgpl-3.0.txt>.
 */
package ua.metallic.ofcchart.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.HTML;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.WidgetCanvas;
import com.smartgwt.client.widgets.Window;
import com.smartgwt.client.widgets.plugins.Flashlet;
import ua.metallic.ofcchart.client.event.ChartClickEvent;
import ua.metallic.ofcchart.client.event.ChartClickHandler;
import ua.metallic.ofcchart.client.event.DataValueEvents;
import ua.metallic.ofcchart.client.event.KeyClickHandler;
import ua.metallic.ofcchart.client.model.ChartData;

import java.util.HashMap;

/**
 * A gwt chart widget based on Open Flash Chart.</br></br>
 * <p/>
 * Create the ChartWidget and add anywhere a GWT widget can be used. Use the
 * model {@link ua.metallic.ofcchart.client.model.ChartData} to build a chart and then pass to
 * {@link ChartWidget#setChartData(ua.metallic.ofcchart.client.model.ChartData)} and it will generate and set the
 * correct JSON data.
 * <p/>
 * You can also set JSON via the {@link #setJsonData(String)} method.
 */
public class ChartWidget extends Flashlet {


    private static int count = 0;

    private boolean hasFlashPlayer = flashAvailable();
    private String jsonData = ChartFactory.BLANK_CHART_JSON_DATA;
    private static String flashurl = "/open-flash-chart-full-embedded-font.swf";
    private ChartData chartData;
    String swfId;
    private Window window;
    WidgetCanvas widgetCanvas;

    /**
     * Creates a new ChartWidget. *
     */
    public ChartWidget() {
        swfId = "swfID_" + count;
        setID(swfId + "_");
        ++count;
        setSrc(GWT.getModuleBaseURL() + flashurl);
        String width = "100%";
        String height = "100%";
        setSize(width, height);
        HashMap<String, String> hashMap = new HashMap<String, String>();
        // put id
        hashMap.put("id", swfId);
        hashMap.put("FlashVars", "id=" + swfId);
        setParams(hashMap);
        // register chart
        ChartFactory.get().register(this);
    }

    private void initDefaultSaveImageWindow() {
        window = new Window();
        window.setIsModal(true);
        window.setShowMinimizeButton(false);
        window.setShowCloseButton(true);
        window.setAutoCenter(true);
        window.setShowModalMask(true);
        window.setShowResizer(true);
        window.setWidth(getWidth());
        window.setHeight(getHeight());
        window.setTitle("");
    }

    /**
     * Method returns "Save image locally" window, so it can be customized(i18nzed)
     *
     * @return Window object
     */
    public Window getSaveImageWindow() {
        if (window == null)
            initDefaultSaveImageWindow();
        return window;
    }

    /**
     * Gets the current JSON data for this chart.
     *
     * @return a JSON string
     */
    public String getJsonData() {
        return jsonData;
    }

    /**
     * @return the swfId
     */
    public String getSwfId() {
        return swfId;
    }


    /**
     * Obtains raw image data of this chart. Call returns null if the flash
     * chart is not loaded.
     *
     * @return String chart image data
     */
    public String getImageData() {
        if (hasFlashPlayer) {
            return getImageData(getPluginHandleLocal());
        }
        return null;
    }


    /**
     * Sets this charts ChartData and processes it for handlers/events
     *
     * @param cd the ChartData model
     */
    public void setChartData(ChartData cd) {
        this.chartData = cd;
        for (ua.metallic.ofcchart.client.model.elements.Element e : chartData.getElements()) {
            // add chart click events...
            for (ChartClickHandler cch : e.getChartClickHandlers()) {
                String onclick = "ofc_onclick('" + getSwfId() + "','" + cch.hashCode() + "')";
                e.setOnClick(onclick);
            }
            // add key click events...
            for (KeyClickHandler kch : e.getKeyClickHandlers()) {
                String onclick = "ofc_onclick('" + getSwfId() + "','" + kch.hashCode() + "')";
                e.setKeyOnClick(onclick);
            }
            for (Object o : e.getValues()) {
                if (o instanceof DataValueEvents) {
                    // add data value click events...
                    DataValueEvents dve = (DataValueEvents) o;
                    for (ChartClickHandler ch : dve.getHandlers()) {
                        String onclick = "ofc_onclick('" + getSwfId() + "','" + ch.hashCode() + "')";
                        dve.setOnClick(onclick);
                    }
                }
            }
        }
        setJsonData(chartData.buildJSON().toString());
    }


    /**
     * Sets the JSON data for this chart & updates the chart if ready. Does
     * nothing if the required flash player is not loaded.
     *
     * @param json a JSON string
     */
    public void setJsonData(String json) {
        // System.out.println(json);
        this.jsonData = json;
        if (hasFlashPlayer) {
            loadJSON(jsonData, getPluginHandleLocal());
        }
    }

    private Element getPluginHandleLocal() {
        if (getPluginHandle() == null)
            this.draw();
        return getPluginHandle();
    }

    protected void doOnChartClick(String evt) {
        for (ua.metallic.ofcchart.client.model.elements.Element e : chartData.getElements()) {
            for (Object o : e.getValues()) {
                if (o instanceof DataValueEvents) {
                    DataValueEvents ee = (DataValueEvents) o;
                    for (ChartClickHandler ch : ee.getHandlers()) {
                        if (evt.equals("" + ch.hashCode())) {
                            ch.onClick(new ChartClickEvent());
                        }
                    }
                }
            }
        }
    }

    @Override
    protected void onAttach() {
        ChartFactory.get().register(this);
        super.onAttach();
    }

    @Override
    protected void onDetach() {
        ChartFactory.get().unregister(this);
        super.onDetach();
    }

    private native void loadJSON(String json, Element chartEl) /*-{
        if ('load' in chartEl) {
            chartEl.load(json);
        }
    }-*/;

    private native String getImageData(Element chartEl)
        /*-{
          var data = null;
          if ('get_img_binary' in chartEl) data = chartEl.get_img_binary();
          return data;
      }-*/;

    public void saveImagePopup() {
        if (widgetCanvas != null)
            getSaveImageWindow().removeItem(widgetCanvas);
        widgetCanvas = new WidgetCanvas(new HTML(Canvas.imgHTML("data:image/png;base64," + getImageData())));
        getSaveImageWindow().addItem(widgetCanvas);
        getSaveImageWindow().show();
    }
}